using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;


namespace _3D_Game
{
    public class ModelManager : DrawableGameComponent 
    {
        List<BasicModel> models = new List<BasicModel>();
        List<BasicModel> shots = new List<BasicModel>( );

        float shotMinZ = -3000;//Beyond this point the shot will be taken out of the shot list

       // Spawn variables
       Vector3 maxSpawnLocation = new Vector3(100, 100, -3000);
       int nextSpawnTime = 0;
       int timeSinceLastSpawn = 0;
       float maxRollAngle = MathHelper.Pi / 40;
       int NumSpaceShip = 0;
       int missedThisLevel = 0;
         
       List<ParticleExplosion> explosions = new List<ParticleExplosion>();
       ParticleExplosionSettings particleExplosionSettings = new ParticleExplosionSettings();
       ParticleSettings particleSettings = new ParticleSettings();
       Texture2D explosionTexture;
       Texture2D explosionColorsTexture;
       Effect explosionEffect;

       public ModelManager(Game game)
           : base(game)
       {

       }

        public override void Initialize()
        {
           // Set initial spawn time
           SetNextSpawnTime();

            base.Initialize();
        }

        public override void Update(GameTime gameTime)
        {
           //At the right time create the space ship
           timeSinceLastSpawn += gameTime.ElapsedGameTime.Milliseconds;
           if (timeSinceLastSpawn > nextSpawnTime)
           {
              CreateSpaceShip();
           }
            
           UpdateModels();
           UpdateShots();
           //SpaceShipShot();
           // Update explosions
           UpdateExplosions(gameTime);
           base.Update(gameTime);
        }

        public override void Draw(GameTime gameTime)
        {
            // Loop through and draw each model
            foreach (BasicModel bm in models)
            {
                bm.Draw(((Game1)Game).camera);
            }

            // Loop through and draw each shot
            foreach (BasicModel bm in shots)
            {
                bm.Draw(((Game1)Game).camera);
            }

            // Loop through and draw each particle explosion
            foreach (ParticleExplosion pe in explosions)
            {
                pe.Draw(((Game1)Game).camera);
            }

            base.Draw(gameTime);
        }

        protected override void LoadContent()
        {
            // Load explosion textures and effect
            explosionTexture = Game.Content.Load<Texture2D>(@"Texture\Particle");
            explosionColorsTexture = Game.Content.Load<Texture2D>(@"Texture\ParticleColors");
            explosionEffect = Game.Content.Load<Effect>(@"effects\particle");
            // Set effect parameters that don't change per particle
            explosionEffect.CurrentTechnique = explosionEffect.Techniques["Technique1"];
            explosionEffect.Parameters["theTexture"].SetValue(explosionTexture);
            
            base.LoadContent();

        }

        private void SetNextSpawnTime()
        {
           nextSpawnTime = ((Game1)Game).rnd.Next(900, 2800);
           timeSinceLastSpawn = 0;
        }

        Vector3 position;
        Vector3 direction;
        private void CreateSpaceShip()
        {
            RandomCreatePosDir();
           // Get a random roll rotation between -maxRollAngle and maxRollAngle
           float rollRotation = (float)((Game1)Game).rnd.NextDouble()* maxRollAngle - (maxRollAngle / 2);
           // Add model to the list
           models.Add(new SpiningSpaceShip(Game.Content.Load<Model>(@"models\spaceship"),
              position, direction, 0, 0, rollRotation));

           CreateEnemyShots(position, direction);
           SetNextSpawnTime();
        }

        void RandomCreatePosDir()
        {
            // Generate random position with random X and random Y between -maxX and maxX and -maxY and maxY. Z is always the same for all ships.
            position = new Vector3(((Game1)Game).rnd.Next(
               -(int)maxSpawnLocation.X, (int)maxSpawnLocation.X),
               ((Game1)Game).rnd.Next(
                       -(int)maxSpawnLocation.Y, (int)maxSpawnLocation.Y),
                       maxSpawnLocation.Z);
            // Direction will always be (0, 0, Z), where Z is a random value between minSpeed and maxSpeed
            direction = new Vector3(0, 0, ((Game1)Game).rnd.Next(2, 6));
        }

        void CreateEnemyShots(Vector3 ShotPos, Vector3 ShotDir)
        {
            models.Add(new EnemyShot(Game.Content.Load<Model>(@"models\ammo"),
                ShotPos, ShotDir));
        }

       protected void UpdateModels( )
       {
          // Loop through all models and call Update
          for (int i = 0; i < models.Count; ++i)
          {
             // Update each model
             models[i].Update( );
             // Remove models that are out of bounds
             if (models[i].GetWorld().Translation.Z >
                ((Game1)Game).camera.cameraPosition.Z + 100)
             {
                 models.RemoveAt(i);
                 --i;
             }

          }
       }

       public void AddShot(Vector3 position, Vector3 direction)
       {
           shots.Add(new SpiningSpaceShip(Game.Content.Load<Model>(@"models\ammo"),
           position, direction, 0, 0, 0));
       }

       protected void UpdateShots()
        {
            // Loop through shots
            for (int i = 0; i < shots.Count; ++i)
            {
                // Update each shot
                shots[i].Update( );
                // If shot is out of bounds, remove it from game
                if (shots[i].GetWorld( ).Translation.Z < shotMinZ)
                {
                    shots.RemoveAt(i);
                    --i;
                }
                else
                {
                    // If shot is still in play, check for collisions
                    for (int j = 0; j < models.Count; ++j)
                    {
                        if (shots[i].CollidesWith(models[j].model,models[j].GetWorld( )))
                        {
                            // Collision! add an explosion.
                            CreateExplosion(models[j]);
                            // Collision! remove the ship and the shot.
                            models.RemoveAt(j);
                            shots.RemoveAt(i);
                            --i;
                            ((Game1)Game).AddScore();
                            ((Game1)Game).AddLevel();
                            break;
                         }
                     }
                 }
            }
        }

        private void CreateExplosion(BasicModel Ship)
        {
            ParticleExplosion explosion = new ParticleExplosion(GraphicsDevice,
                                Ship.GetWorld().Translation,
                                ((Game1)Game).rnd.Next(
                                particleExplosionSettings.minLife,
                                particleExplosionSettings.maxLife),
                                ((Game1)Game).rnd.Next(
                                particleExplosionSettings.minRoundTime,
                                particleExplosionSettings.maxRoundTime),
                                ((Game1)Game).rnd.Next(
                                particleExplosionSettings.minParticlesPerRound,
                                particleExplosionSettings.maxParticlesPerRound),
                                ((Game1)Game).rnd.Next(
                                particleExplosionSettings.minParticles,
                                particleExplosionSettings.maxParticles),
                                explosionColorsTexture, particleSettings,
                                explosionEffect);
            explosions.Add(explosion);
        }

        protected void UpdateExplosions(GameTime gameTime)
        {
            // Loop through and update explosions
            for (int i = 0; i < explosions.Count; ++i)
            {
                explosions[i].Update(gameTime);
                // If explosion is finished, remove it
                if (explosions[i].IsDead)
                {
                    explosions.RemoveAt(i);
                    --i;
                }
            }
        }
    }
}
